/**
 * @author Brian Alker
 * @author Edmundo Martinez
 *
 */

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * This class implements the router functionality specified in the project description including:
 * 64 packet capacity
 * service rate of 10ms/pkt
 */
class UDPServer
{
   private List <DatagramPacket> queue1 = Collections.synchronizedList(new LinkedList <DatagramPacket>());
   private List <DatagramPacket> queue2 = Collections.synchronizedList(new LinkedList <DatagramPacket>());
   private static int forwardingInterval = 10;
   private static int maxQueueSize1;
   private static int maxQueueSize2;
   private String args[];
   private int port1, port2;
   private double serviceRatio;

	public UDPServer(String[] in_args) {
      args = in_args;
   }
	
   /**
	 * @param args All packets addressed to port arg0 will be put in queue1. 
	 * All packets addressed to port arg1 will be put in queue2. 
	 * arg2-3 specify the size of queue1 and queue2, respectively; they should add up to 64. 
	 * arg4 is the service ratio for queue1; 
	 * in the event that both queue1 and queue2 contain packets, 
	 * this is the probability of queue being serviced instead of queue2. 
	 */
   public static void main(String args[]) throws Exception{
      UDPServer us = new UDPServer(args);
      us.run();
   }
   public void run() throws Exception
   {
      DatagramSocket serverSocket = new DatagramSocket(9876);
      
      Timer timer = new Timer();
      QueueHandler qhandle = new QueueHandler();
      timer.schedule(qhandle, 0, forwardingInterval);
      
      port1 = Integer.parseInt(args[0]);
      port2 = Integer.parseInt(args[1]);
      maxQueueSize1 = Integer.parseInt(args[2]);
      maxQueueSize2 = Integer.parseInt(args[3]);
      serviceRatio = Double.parseDouble(args[4]);
      
      while(true)
         {
            byte[] receiveData = new byte[128];
            byte[] addressBytes = new byte[4];
            byte[] portBytes = new byte[4];
            byte[] sendData = new byte[128];
            
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            serverSocket.receive(receivePacket);
            System.arraycopy(receivePacket.getData(), 0, addressBytes, 0, 4);
            System.arraycopy(receivePacket.getData(), 4, portBytes, 0, 4);
            InetAddress IPAddress = InetAddress.getByAddress(addressBytes);
            int port = byteArrayToInt(portBytes);
            sendData = receivePacket.getData();               
            DatagramPacket sendPacket =
               new DatagramPacket(sendData, sendData.length, IPAddress, port);
            
            
            if((port == port1 || maxQueueSize2 == 0) && queue1.size() < maxQueueSize1) {
               queue1.add(sendPacket);
            } else if(port == port2 && queue2.size() < maxQueueSize2) {
               queue2.add(sendPacket);
            }
         }
   }
   
   public static int byteArrayToInt(byte a[]) {
      return (((int)(a[3])) & 0xFF)             |
            ((((int)(a[2])) << 8) & 0xFF00)     |
            ((((int)(a[1])) << 16) & 0xFF0000)  |
            ((((int)(a[0])) << 24) & 0xFF000000);
   }
   
   private class QueueHandler extends TimerTask {
      
      public QueueHandler() throws Exception {
      }
      
      /**
       * Gets called every 10ms to forward 1 packet from the queue
       */
      public void run() {
         if(!queue1.isEmpty() && !queue2.isEmpty()) {
            if(Math.random() < serviceRatio) {
               try {               
                  DatagramSocket serverSocket = new DatagramSocket();
                  serverSocket.send(queue1.remove(0));
                  serverSocket.close();
               } catch (Exception e) {
                  e.printStackTrace();
               }
            } else {
               try {               
                  DatagramSocket serverSocket = new DatagramSocket();
                  serverSocket.send(queue2.remove(0));
                  serverSocket.close();
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         } else if(queue1.isEmpty() && !queue2.isEmpty()) {
            try {               
               DatagramSocket serverSocket = new DatagramSocket();
               serverSocket.send(queue2.remove(0));
               serverSocket.close();
            } catch (Exception e) {
               e.printStackTrace();
            }            
         } else if(queue2.isEmpty() && !queue1.isEmpty()) {
            try {               
               DatagramSocket serverSocket = new DatagramSocket();
               serverSocket.send(queue1.remove(0));
               serverSocket.close();
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }
      
   }
}